package com.syh.jn.scm.infrastructure.wrapper;

import cn.redscarf.commons.util.other.ExtStringUtils;
import cn.redscarf.commons.wrapper.Wrapper;
import cn.redscarf.commons.wrapper.expression.conditionExpression.*;
import cn.redscarf.commons.wrapper.expression.logicalExpression.AND;
import cn.redscarf.commons.wrapper.expression.logicalExpression.NOT;
import cn.redscarf.commons.wrapper.expression.logicalExpression.OR;
import cn.redscarf.commons.wrapper.expression.orderExpression.Order;
import cn.redscarf.commons.wrapper.expression.support.Expression;
import cn.redscarf.commons.wrapper.expression.support.LogicalExpression;
import cn.redscarf.commons.wrapper.expression.support.SimpleExpression;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

public abstract class BaseResolve {

  protected Wrapper wrapper;
  protected ParseMethods parseMethods;

  public BaseResolve(Wrapper wrapper) {
    this.wrapper = wrapper;
  }

  public BaseResolve(Wrapper wrapper, ParseMethods parseMethods) {
    this(wrapper);
    this.parseMethods = parseMethods;
  }

  private String parseOrder(List<Order> orderList) {
    StringBuffer orderSb = new StringBuffer();

    Iterator<Order> iterator = orderList.iterator();
    while (iterator.hasNext()) {
      orderSb.append(orderSb.length() == 0 ? " ORDER BY " : ",");
      Order order = iterator.next();
      orderSb.append(this.parsePropertyName(order.propertyName));
      orderSb.append(order.ascending ? " ASC " : " DESC ");
    }
    return orderSb.toString();
  }

  protected String parseCriterion(List<Expression> expressionList) {
    StringBuffer sqlSb = new StringBuffer();

    if (expressionList == null || expressionList.isEmpty()) {
      return "";
    }

    Iterator<Expression> iterator = expressionList.iterator();
    while (iterator.hasNext()) {
      sqlSb.append(this.parseExpression(iterator.next()));
      if (iterator.hasNext()) {
        sqlSb.append(wrapper.defaultLogic == Wrapper.Logic.AND ? " AND " : " OR ");
      }
    }

    return sqlSb.toString();
  }

  protected StringBuffer parseExpression(Expression expression) {
    StringBuffer sb = new StringBuffer();
    if (expression instanceof LogicalExpression) {
      List<Expression> innerExpressionList = ((LogicalExpression) expression).expressions;

      Iterator<Expression> iterator = innerExpressionList.iterator();
      while (iterator.hasNext()) {

        StringBuffer innerBuffer = this.parseExpression(iterator.next());

        if (expression instanceof OR && innerBuffer.length() > 0) {
          sb.append(innerBuffer);
          if (iterator.hasNext()) {
            sb.append(" OR ");
          }
        }

        if (expression instanceof AND && innerBuffer.length() > 0) {
          sb.append(innerBuffer);
          if (iterator.hasNext()) {
            sb.append(" AND ");
          }
        }

        if (expression instanceof NOT && innerBuffer.length() > 0) {
          sb.append("NOT ");
          sb.append(innerBuffer);
        }
      }

      if (innerExpressionList.size() > 1) {
        sb.insert(0, "(");
        sb.append(")");
      }
    }

    sb.append(this.parseCondition(expression));

    return sb;
  }

  protected StringBuffer parseCondition(Expression criterion) {

    StringBuffer sb = new StringBuffer();

    if (criterion instanceof Between) {
      Between expression = (Between) criterion;
      sb.append(String.format("%s BETWEEN %s AND %s",
              this.parsePropertyName(expression.propertyName),
              new StringBuilder().append("'").append(expression.low).append("'").toString(),
              new StringBuilder().append("'").append(expression.high).append("'").toString()
      ));
    }

    if (criterion instanceof In) {
      In expression = (In) criterion;

      List list = Arrays.asList(expression.values).stream().map((item) -> {
        return new StringBuilder().append("'").append(item).append("'").toString();
      }).collect(Collectors.toList());
      String valuesString = String.join(",", list);

      sb.append(String.format("%s IN (%s)", this.parsePropertyName(expression.propertyName), valuesString));

    }
    if (criterion instanceof NotIn) {
      NotIn expression = (NotIn) criterion;

      List list = Arrays.asList(expression.values).stream().map((item) -> {
        return item.toString();
      }).collect(Collectors.toList());
      String valuesString = String.join(",", list);

      sb.append(String.format("%s NOT IN (%s)", this.parsePropertyName(expression.propertyName), valuesString));

    }
    if (criterion instanceof Like) {
      Like expression = (Like) criterion;
      sb.append(String.format("%s LIKE %s", this.parsePropertyName(expression.propertyName), new StringBuilder().append("'").append(expression.value).append("'").toString(), "'"));

    }
    if (criterion instanceof IsNull) {
      IsNull expression = (IsNull) criterion;
      sb.append(String.format("%s IS NULL", this.parsePropertyName(expression.propertyName)));
    }
    if (
            criterion instanceof NotNull) {
      NotNull expression = (NotNull) criterion;
      sb.append(String.format("%s IS NOT NULL", this.parsePropertyName(expression.propertyName)));
    }
    // 6
    if (
            criterion instanceof PropertyExpression) {
      PropertyExpression expression = (PropertyExpression) criterion;
      sb.append(String.format("%s %s %s", this.parsePropertyName(expression.propertyName), expression.op, this.parsePropertyName(expression.otherPropertyName)));
    }
    // 7
    if (criterion instanceof SimpleExpression) {
      SimpleExpression expression = (SimpleExpression) criterion;
      sb.append(String.format("%s %s %s", this.parsePropertyName(expression.propertyName), expression.op, new StringBuilder().append("'").append(expression.value).append("'").toString()));

    }

    return sb;
  }

  protected String parsePropertyName(String propertyName) {
    String innerPropertyName = null;
    if (this.parseMethods != null) {
      innerPropertyName = this.parseMethods.customParsePropertyName(propertyName);
    }

    if (ExtStringUtils.isEmpty(innerPropertyName)) {
      innerPropertyName = propertyName;
    }

    return innerPropertyName;
  }


  public String resolve() {
    String queryCondition = this.resolveQueryCondition();
    queryCondition = StringUtils.isEmpty(queryCondition) ? "" : " AND " + queryCondition;
    return queryCondition + this.resolveOrderCondition();
  }

  public String resolveQueryCondition() {
    return this.parseCriterion(wrapper.queryCondition);
  }

  public String resolveOrderCondition() {
    return this.parseOrder(wrapper.orderArrayList);
  }


  public interface ParseMethods {
    String customParsePropertyName(String propertyName);
  }

}
